package com.ywj.chatroom.multiroom.protocol;

import com.ywj.chatroom.multiroom.message.Message;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;

//协议，消息按这种格式传递和解析.必须和 LengthFieldBasedFrameDecoder 一起使用，确保接到的 ByteBuf 消息是完整的
@Slf4j
@ChannelHandler.Sharable
public class MessageCodecSharable extends MessageToMessageCodec<ByteBuf, Message> {
    @Override
    protected void encode(ChannelHandlerContext ctx, Message msg, List<Object> outList) throws Exception {
        ByteBuf out = ctx.alloc().buffer();
        // 1. 4字节的魔数，最好和java那些不一样，自己独特的
        out.writeBytes(new byte[]{1, 2, 3, 4});
        // 2. 1字节的版本,
        out.writeByte(1);
        // 3. 1字节的序列化方式，我们约定jdk是0, json是1
        out.writeByte(0);
        // 4. 1字节的指令类型，聊天消息还是登陆消息等等，加以区分
        out.writeByte(msg.getMessageType());
        // 5. 4个字节的指令请求序号，目前还用不上
        out.writeInt(msg.getSequenceId());
        // 无意义，对齐填充
        out.writeByte(0xff);
        // 6. 获取内容的字节数组，使用序列化的方式，也可以使用转为JSON的方式
        //    这里选择了序列化的方式，因为上面序列化方式我们选择了JDK
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(msg);
        //oos把msg序列化结果写到ObjectOutputStream，ObjectOutputStream再把结果写到ByteArrayOutputStream
        byte[] bytes = bos.toByteArray();
        // 7. 字节数组的长度
        out.writeInt(bytes.length);
        // 8. 写入内容
        out.writeBytes(bytes);

        //除了内容可变，其他都是固定的，字节大小
        //4+1+1+1+4+4 = 15个字节，我们使用out.writeByte(0xff) +1个字节凑够16个，仅仅是对齐用的，有个规定是2的n次方倍才叫专业
        outList.add(out);
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        int magicNum = in.readInt();
        byte version = in.readByte();
        byte serializerType = in.readByte();
        byte messageType = in.readByte();
        int sequenceId = in.readInt();
        in.readByte();
        int length = in.readInt();
        //字节数据转变为对象
        byte[] bytes = new byte[length];
        in.readBytes(bytes, 0, length);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
        Message message = (Message) ois.readObject();
        out.add(message);
    }
}
